﻿using AutoMapper;
using HamcoWcs.Contract.ContractCommon;
using HamcoWcs.Contract.ContractMainTask;
using HamcoWcs.Contract.ContractMes;
using HamcoWcs.Contract.ContractPcon.IPconDbMsgParsers;
using HamcoWcs.Contract.ContractSrm;
using HamcoWcs.Contract.MachineStatus;
using HamcoWcs.DAL;
using HamcoWcs.Entitys.BaseConfigs;
using HamcoWcs.Entitys.Logss;
using HamcoWcs.Models.BaseConfigs;
using HamcoWcs.Models.Common;
using HamcoWcs.Models.Common.Enums;
using HamcoWcs.Models.DeviceMsgs.SrmMsgs;
using HamcoWcs.Models.Devices;
using HamcoWcs.Models.Monitors;
using HamcoWcs.Models.NodeInfos;
using HamcoWcs.Service.Common;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace HamcoWcs.Workers.SrmWorkers
{
    /// <summary>华星PPK项目堆垛机后台服务</summary>
    [DisallowConcurrentExecution]
    public partial class PpkSrmWorker : WorkerBase
    {
        private void DoWork(NodeInfoDto node)
        {
            try
            {
                node.StatusChangeEvent += OnMachineStateChanged;
                //DoTest(node);
                var a = DealRecv(node);
                if (!a)
                {
                    node.StatusChangeEvent -= OnMachineStateChanged;
                    return;
                } 
                DealGlobalConfig(node);
                DealMonitor(node);
                DealTask(node);
                DealSend(node);
                DealGlobalConfigSend(node);
                node.StatusChangeEvent -= OnMachineStateChanged;
            }
            catch (Exception e)
            {
                node.StatusChangeEvent -= OnMachineStateChanged;
                _logger.AddSystemErrorLog("PpkSrmWorker->DoWork:" + e.Message);
                return;
            }
        }

        private void DealTeach(NodeInfoDto node)
        {
            try
            {
                if (GlobalValue.SrmTeachRequestQueue.Count <= 0) return;
                var m = GlobalValue.SrmTeachRequestQueue.Peek();
                var target = "";
                if (m.Row == 1 || m.Row == 2)
                    target = "CJSTW100";
                else
                    target = "CJSTW200";

                if (target == node.NodeName)
                {
                    foreach (var srm in node.Srms)
                    {

                    }
                }

            }
            catch (Exception)
            {

            }
        }



        private void DealSend(NodeInfoDto node)
        {
            //foreach (var srm in node.Srms)
            //{
            var srmPlc = node.SrmPlc;
            if (!srmPlc.ConnectionStatus) srmPlc.ConnectToPlc();
            if (!srmPlc.ConnectionStatus) return;

            try
            {
                DealSend(srmPlc);
            }
            catch (Exception ex)
            {
                _logger.AddSystemErrorLog($"PpkSrmWorker->DealSend:{ex.Message}");
                srmPlc.DisconnectToPlc();
            }
        }

        private void DealGlobalConfigSend(NodeInfoDto node)
        {
            var plc = node.SrmPlc;

            if (GlobalValue.SrmGlobalConfigs.Values.Where(x => x.PlcName == node.SrmPlc.PlcName).Any(x => x.IsChange))
            {
                foreach (var cfg in GlobalValue.SrmGlobalConfigs.Values.Where(x => x.PlcName == node.SrmPlc.PlcName && x.IsChange))
                {
                    if (!cfg.IsChange) continue;
                    if (string.IsNullOrEmpty(cfg.NewValue))
                    {
                        _logger.AddSystemErrorLog($"PpkSrmWorker->DealGlobalConfigSend:{cfg.Name},类型{cfg.Type}，处理失败:新值为空");
                    }


                    if (cfg.Type == "INT")
                    {
                        UInt16 v = UInt16.Parse(cfg.NewValue);
                        var b = BitConverter.GetBytes(v).ToArray();
                        var res = plc.Client.Write("D" + cfg.Offset, b);

                        if (!res.IsSuccess)
                        {
                            _logger.AddSystemErrorLog($"PpkSrmWorker->DealGlobalConfigSend:{cfg.Name},类型{cfg.Type}，处理失败:写入DB失败");
                        }

                        cfg.IsChange = false;
                    }
                    else
                    {
                        _logger.AddSystemErrorLog($"PpkSrmWorker->DealGlobalConfigSend:{cfg.Name},类型{cfg.Type}，处理失败：无当前类型的处理方法");
                    }

                }
            }
        }


        private void DealSend(SrmPlc srmPlc)
        {
            //return;
            //检查连接状态
            if (!srmPlc.ConnectionStatus) return;
            //获取DB对象
            //var dbInfo = srmPlc.DbInfo.FirstOrDefault(x => x.DbName == $"{}_send");
            //if (dbInfo == null) return;

            var msgEntity = _wcsDbContext.DeviceMsgQueues.OrderByDescending(x => x.CreatedTime)
                .FirstOrDefault(x => x.PlcName == srmPlc.PlcName);



            if (msgEntity == null) return;

            // 读取DB块，检查上次request是否处理完成
            var recvBuff = srmPlc.Client.Read("D" + msgEntity.StartPoint, 23).Content;
            var msg = new SrmSendMsg(recvBuff, false);

            // [20230626][Obsolete]PLC未处理则跳过
            //if (msg.Request != 0) return;
            // [20230626] 新增response number 若number=response则下发新任务
            if (msg.Number != msg.Response)
                return;

            //srmPlc.IsFirstMessage = false;

            ushort number = (ushort)((msg.Number == ushort.MaxValue) ? 0 : msg.Number + 1);
            // 上次处理完成，直接写入DB块。
            var sendmsgDto = JsonConvert.DeserializeObject<SrmSendMsg>(msgEntity.MsgValue);
            sendmsgDto.Number = number;
            byte[] sendMsg = sendmsgDto.Encode();

            if (sendMsg.Length != GlobalValue.SRM_SEND_MSG_SIZE) return;

            var res = srmPlc.Client.Write("D" + msgEntity.StartPoint, sendMsg);
            if (!res.IsSuccess)
            {
                //发送不成功
                _logger.AddSystemErrorLog($"{srmPlc.PlcName}写入DB信息失败");
                srmPlc.DisconnectToPlc();
                return;
            }

            _wcsDbContext.DeviceMsgQueues.Remove(msgEntity);
            //_loggerService.AddDeviceMsgLog(srmPlc.PlcName, msgEntity.DeviceName, "", sendmsgDto.UnitId, msgEntity.MsgValue, "send");

            var srm = _asrs.Srms.FirstOrDefault(x => x.SrmName == msgEntity.DeviceName);

            _logger.AddCommunicationLog(new CommunicationLog("WCS >> SRM")
            {
                TransactionId = $"{DateTime.Now:yyyyMMddHHmmssffffff}",
                Sender = "WCS",
                Receiver = srm.DeviceName,
                Content = msgEntity.MsgValue,
                MessageName = ((EnumSrmSendMsg)sendmsgDto.Request).ToString(),
                MessageStatus = $"number:{number}",
                MessageType = "",
                Return = "",
                UnitId = sendmsgDto.UnitId,
            });

            Thread.Sleep(50);
            _wcsDbContext.SaveChanges();
        }

        private void DealMonitor(NodeInfoDto node)
        {
            var srmdata = _monitorBaseService.GetMonitorData().MonitorSrmDatas;
            foreach (var srm in node.Srms)
            {
                var srmmonitor = srmdata.FirstOrDefault(x => x.Name == $"{srm.MachineName}-{srm.PortName}");
                if (srmmonitor == null)
                {
                    srmdata.Add(new MonitorSrmData(srm));
                }
                else
                {
                    srmmonitor.Update(srm);
                }
            }
        }





        /// <summary>处理srm锁定和取消锁定</summary>
        private void HandelSrmBlock(SrmDto srm, SrmRecvMsg msg)
        {
            var k = $"{srm.Aisle.NodeName}RECOVERYFLAG";
            // 禁用srm时
            if (srm.Enabled == 1 && msg.Enabled == 0)
            {
                try
                {
                    HandelSrmBlockTask(srm);
                    _locationService.SrmOccupy(srm.SrmName);
                    _logger.AddWcsRunLog(srm.DeviceName, "设宕");
                }
                catch (Exception)
                {
                    _logger.AddSystemErrorLog($"{srm.DeviceName}设宕是WCS处理失败");
                }

            }

            // 解禁srm时
            if (srm.Enabled == 0 && msg.Enabled == 1)
            {
                _config.SetConfigMsg(k, "0");
                _locationService.SrmUnoccupy(srm.SrmName);
                _logger.AddWcsRunLog(srm.DeviceName, "恢复运行");
            }
        }



        private void HandelSrmBlockTask(SrmDto srm)
        {
            // 获取当前堆垛机未开始的任务
            var subtasks = _wcsDbContext.SubTasks
                .Where(x => x.PlcName == srm.PlcName)
                .Where(x => x.TaskStatus == EnumSubTaskStatus.Ready.ToString())
                .ToList();

            for (int i = subtasks.Count - 1; i >= 0; i--)
            {
                try
                {
                    var task = subtasks[i];
                    var fb = 0;
                    var tb = 0;
                    if (task.FromLoc.IsShelf())
                        fb = int.Parse(task.TaskFrom[2..5]);
                    if (task.ToLoc.IsShelf())
                        tb = int.Parse(task.ToLoc[2..5]);

                    // 如果无法到达则移除任务，不改变执行堆垛机
                    if (srm.BlockedBays.Contains(fb) || srm.BlockedBays.Contains(tb))
                    {
                        subtasks.Remove(task);
                    }
                }
                catch (Exception)
                {
                    continue;
                }
            }

            foreach (var subtask in subtasks)
            {
                subtask.PlcName = srm.Aisle.NodeName;
                if (subtask.IsNeedSection)
                {
                    var loc = _wcsDbContext.WcsLocations.Where(x => x.WcsLocName == subtask.HandOffShelf).FirstOrDefault();

                    subtask.HandOffShelf = "";
                    subtask.IsNeedSection = false;

                    if (loc.ZoneName.Contains("Z6"))
                    {
                        loc.LocStatus = (int)EnumLocStatus.HandOff;
                    }
                    else
                    {
                        loc.LocStatus = (int)EnumLocStatus.Empty;
                    }

                    loc.Remark = $"{srm.DeviceName}设宕时重新分配堆垛机搬送时取消中转搬送";
                }
            }
            _wcsDbContext.SaveChanges();
        }

        private void DealGlobalConfig(NodeInfoDto node)
        {
            if (!node.SrmPlc.ConnectionStatus) return;

            var cfgs = GlobalValue.SrmGlobalConfigs.Values.Where(x => x.PlcName == node.SrmPlc.PlcName).ToList();
            var globalData = _monitorBaseService.GetMonitorData().GlobalConfig;
            var pis = globalData.GetType().GetProperties();

            foreach (var cfg in cfgs)
            {
                var v = node.SrmPlc.Client.Read("D" + cfg.Offset, (ushort)cfg.Length);
                if (!v.IsSuccess) return;
                var value = BitConverter.ToUInt16(v.Content);
                cfg.CurrentValue = value.ToString();
                if (GlobalValue.SrmHmiSpeedDic.ContainsKey(cfg.Name))
                {
                    try
                    {
                        var res = node.SrmPlc.Client.Read("D" + GlobalValue.SrmHmiSpeedDic[cfg.Name], 2);
                        if (!res.IsSuccess) return;
                        var hmiSpeed = BitConverter.ToUInt16(res.Content);
                        var speed = hmiSpeed * value / 100;
                        var name = cfg.Name[..11] + "ACTUALSPEED";
                        var p = pis.FirstOrDefault(x => x.Name == name);
                        if (p is null) continue;
                        p.SetValue(globalData, speed.ToString());
                        //_logger.AddWcsRunLog("设置速度", $"hmiSpeed = {hmiSpeed} value = {value}  speed = {speed}");
                    }
                    catch (Exception e)
                    {
                        _logger.AddWcsRunLog("设置速度",$"ErrorMessage = {e.Message}");
                    }


                }
                var pi = pis.FirstOrDefault(x => x.Name == cfg.Name);
                if (pi is null) continue;
                pi.SetValue(globalData, cfg.CurrentValue);
            }
        }


        public override Task Execute(IJobExecutionContext context)
        {
            var nodeName = context.MergedJobDataMap.GetString("eqptSymbol");    // CJSTW100 | CJSTW200
            var node = _asrs.Nodes.FirstOrDefault(x => x.DeviceType == EnumNodeType.srm && x.NodeName == nodeName);
            DoWork(node);
            return Task.CompletedTask;
        }




        private readonly AutomaticStorageInfo _asrs;
        private readonly IPathService _pathService;
        private readonly ISrmMsgService _srmMsgService;
        private readonly ISrmParseFactory _srmParseFactory;
        private readonly IBaseTaskService _baseTaskService;
        private readonly IBaseSrmTaskService _srmBaseService;
        private readonly IMonitorBaseService _monitorBaseService;
        private readonly ISrmTaskFinderFactory _srmTaskFinderFactory;
        private readonly ILocationService _locationService;
        private readonly IBaseConfigService _config;
        private readonly IMesCommunicateService _mesCommunicateService;
        private readonly IBaseMainTaskService _baseMainTaskService;
        private readonly IMachineStateService _machineStateService;
        private static object _globalConfigsLock = new object();

        public PpkSrmWorker(WcsDbContext wcsDbContext, IMapper mapper, ILoggerService logger,
            IBaseSrmTaskService srmBaseService, IBaseTaskService baseTaskService,
            IMonitorBaseService monitorBaseService, ISrmMsgService srmMsgService,
             IPathService pathService, ISrmTaskFinderFactory srmTaskFinderFactory, AutomaticStorageInfo asrs, ISrmParseFactory srmParseFactory, ILocationService locationService, IBaseConfigService config, IMesCommunicateService mesCommunicateService, IBaseMainTaskService baseMainTaskService,
             IMachineStateService machineStateService) : base(wcsDbContext, mapper, logger)
        {
            _srmBaseService = srmBaseService;
            _baseTaskService = baseTaskService;
            _monitorBaseService = monitorBaseService;
            _srmMsgService = srmMsgService;
            _pathService = pathService;
            _srmTaskFinderFactory = srmTaskFinderFactory;
            _asrs = asrs;
            _srmParseFactory = srmParseFactory;
            _locationService = locationService;
            _config = config;
            _mesCommunicateService = mesCommunicateService;
            _baseMainTaskService = baseMainTaskService;
            _machineStateService = machineStateService;

            if (GlobalValue.SrmGlobalConfigs.Count == 0)
            {
                lock (_globalConfigsLock)
                {
                    var cfgs = _wcsDbContext.Set<GlobalConfig>().Where(x => x.PlcType == "srm").ToList();
                    var cfgmodels = _mapper.Map<List<GlobalConfigModel>>(cfgs);

                    foreach (var cfg in cfgmodels)
                    {
                        GlobalValue.SrmGlobalConfigs.Add(cfg.Name, cfg);
                    }
                }
            }
        }
    }
}
